<form role="form" id="task-form-formio">
    <!--    TODO make the stylesheets configured from the build so people can change what CSS to load-->
    <link rel="stylesheet" href="/forms/formio.full-4.11.3.min.css">
    <link rel="stylesheet" href="/forms/font-awesome-4.7.0/css/font-awesome.min.css">

    <!-- TODO is it better to have the formio div in the <form> element or outside of it?   -->
    <div id="formio"></div>

    <script cam-script type= "text/form-script">
<!--    <script>-->
        let submissionSuffix = '_submission'
        let startFormVariableName = 'startForm' + submissionSuffix
        let formLocationDeploymentParam = 'deployment'
        let formLocationLocalPathParam = 'path'
        let fetchVariableKey = 'fetchVariable'
        let camVariableNameKey = 'camVariableName'
        let stringifyKey = 'stringify'
        let formioDivId = 'formio'
        let submitAsTransientVariableParam = 'transient'
        let submitAsTransientVariableDefault = false
        let submissionVariableNameParam = 'var'
        let removePrivateValidations = true
        let firstSubmit = true
        let subFormKey = 'subform'
        let subFormHideButtonsKey = 'hideButtons'
        let bpmnEscalationCodeKey = "_escalationCode"
        let bpmnEscalationDefaultEscalationCode = "default"
        let bpmnEscalationNonInterruptSuffixDefault = "_escalation"
        let bpmnEscalationInterruptSuffixDefault = "_escalation"
        let bpmnErrorCodeKey = "_errorCode"
        let bpmnErrorDefaultErrorCode = "default"
        let bpmnErrorMessageKey = "_errorMessage"
        let bpmnErrorDefaultErrorMessage = null

        function getParameterByName(name, url) {
            if (!url) url = window.location.href;
            name = name.replace(/[\[\]]/g, '\\$&');
            const regex = new RegExp('[?&]' + name + '(=([^&#]*)|&|#|$)'),
                results = regex.exec(url);
            if (!results) return null;
            if (!results[2]) return '';
            return decodeURIComponent(results[2].replace(/\+/g, ' '));
        }

        function isStartForm(camForm){
            return camForm.taskId == null;
        }

        function hideStartFormStartButton(){
            let element = document.querySelector("body > div.modal.fade.ng-scope.ng-isolate-scope.in > div > div > div.modal-footer.ng-scope > div > div.col-xs-8 > button.btn.btn-primary.ng-binding")
            element.classList.add("hide-form-actions")
        }

        function hideUserTaskFormCompleteButton(){
            let elements = document.getElementsByClassName("form-actions")
            _.forEach(elements, element => {
                element.classList.add("hide-form-actions")
            })
        }

        function clearVariableManager() {
            let variables = $scope.camForm.variableManager.variables;
            for (let v in variables) {
                $scope.camForm.variableManager.destroyVariable(v);
            }
        }

        /**
         * Removes private/secret marked validations from the provided schema.
         */
        function cleanPrivateValidations(schema) {
            if (removePrivateValidations){
                FormioUtils.eachComponent(schema.components, component => {
                    if (component.validate && component.validate.customPrivate) {
                        delete component.validate.custom;
                    }
                })
            }
        }

        /**
         * Returns a formio schema object
         * @returns {Promise<formioShchema>}
         */
        function getSchema(processDefId, formUrl) {
            return new Promise(resolve => {
                inject(['$http', 'Uri', function ($http, Uri) {
                    // let processDefId = camForm.processDefinitionId;

                    $http.get(Uri.appUri('engine://engine/:engine/process-definition/' + processDefId)).then(function (response) {

                        // let formNameDeployment = getParameterByName(formLocationDeploymentParam, camForm.options.formUrl);
                        let formNameDeployment = getParameterByName(formLocationDeploymentParam, formUrl);
                        let formLocDeployment = function () {
                            return formNameDeployment !== null;
                        }

                        // let formNamePath = getParameterByName(formLocationLocalPathParam, camForm.options.formUrl);
                        let formNamePath = getParameterByName(formLocationLocalPathParam, formUrl);
                        let formLocPath = function () {
                            return formNamePath !== null;
                        }

                        if (formLocDeployment() === false && formLocPath() === false) {
                            //@TODO bubble this to formio errors
                            console.error("No valid form json location was found for formio. (could not deployment or path params in form key)")
                            showNotification('error', 5000, "Invalid Form JSON", "No valid form json location was found for formio. (could not deployment or path params in form key)")
                        }

                        let formName = formLocDeployment() === true ? formNameDeployment : formNamePath
                        let formType = formLocDeployment() === true ? formLocationDeploymentParam : formLocationLocalPathParam

                        return {deploymentId: response.data.deploymentId, formName: formName, formType: formType}

                    }).then(function (details) {

                        if (details.formType === formLocationDeploymentParam) {
                            $http.get(Uri.appUri('engine://engine/:engine/deployment/' + details.deploymentId + '/resources')).then(function (response) {
                                let formResource = response.data.find(i => i.name === details.formName);

                                if (formResource === undefined) {
                                    console.error('Unable to find resource with name: ' + details.formName);
                                    showNotification('error', 5000, "Cannot find resource", "Unable to load form: unable to find resource in deployment with name " + details.formName)
                                }

                                return {deploymentId: details.deploymentId, resourceId: formResource.id}

                            }).then(function (ids) {
                                $http.get(Uri.appUri('engine://engine/:engine/deployment/' + ids.deploymentId + '/resources/' + ids.resourceId + '/data')).then(function (response) {
                                    let schema = response.data
                                    cleanPrivateValidations(schema) // This would be removed in future, but currently there is no server API to clean the files
                                    resolve(schema)
                                });
                            });

                        } else if (details.formType === formLocationLocalPathParam) {
                            let schema = $.getJSON(details.formName)
                            cleanPrivateValidations(schema) // This would be removed in future, but currently there is no server API to clean the files
                            resolve(schema)

                        } else {
                            console.error("Unknown form type was provided: " + details.formType)
                            showNotification('error', 5000, "Unknown Form Type", "Unable to load form: unknown form type was provided: " + details.formType)
                        }
                    });
                }]);
            })
        }


        function getVariables(varNames) {
            //@TODO Add error handling when variables are not found.  Should be bubbled to formio form errors view.
            return new Promise(resolve => {
                if (camForm.taskId != null) {
                    inject(['$http', 'Uri', function ($http, Uri) {
                        let taskId = camForm.taskId;

                        let variables = {}
                        let promises = []

                        // _.forEach(varNames, function (varName) {
                        //     if (!variables.hasOwnProperty(varName)) {
                                let promise = $http.get(Uri.appUri('engine://engine/:engine/task/' + taskId + '/form-variables/'), {
                                    params: {
                                        deserializeValues: false,
                                        variableNames: varNames.join(",")
                                    }
                                })
                                promises.push(promise)
                                promise.then(response => {
                                    _.forOwn(response.data, function(value, key) {
                                        if (value.type === 'Json') {
                                            value.value = JSON.parse(value.value)
                                        }
                                        variables[key] = value.value
                                    } );
                                    // variables = response.data
                                })
                            // }

                        // })
                        Promise.all(promises).then(function () {
                            resolve(variables)
                        })
                    }])
                } else {
                    resolve({})
                }

            })
        }

        function showNotification(type, duration, status, message){
            inject(['Notifications', function(Notifications) {
                Notifications.addMessage({
                    type: type,
                    duration: duration,
                    status: status,
                    message: message
                });
            }]);
        }

        function getDefaultValues(variablesStore, submissionObject, schema){
            let defaultValues = {data: {}}
            FormioUtils.eachComponent(schema.components, component => {
                if (component.hasOwnProperty('properties')){
                    if (component.properties.hasOwnProperty(camVariableNameKey)) {
                        let defaultValue = _.get(variablesStore, component.properties[camVariableNameKey])
                        let key = component.key

                        // @TODO review how to make this appear more quickly
                        if (defaultValue === undefined){
                            showNotification('error', null, 'Unable to find default value in variables',
                            'form-variables data did not return expected default value in variables in key ' + component.properties[camVariableNameKey] )
                        }

                        if (component.properties[stringifyKey] === 'true'){
                            // @TODO do a check if the value is a object!
                            defaultValue = JSON.stringify(defaultValue)
                        }

                        defaultValues.data[key] = defaultValue
                    }
                }
            }, true)
            return defaultValues
        }

        function isJsonString(str) {
            try {
                JSON.parse(str);
            } catch (e) {
                return false;
            }
            return true;
        }

        /**
         * suffix is used for use cases like async/non-interrupting escalations
         */
        function setupSubmissionVariables(submission, suffix) {
            let customSubmissionVariableName = getParameterByName(submissionVariableNameParam, camForm.options.formUrl);
            let transientVariableParamValue = getParameterByName(submitAsTransientVariableParam, camForm.options.formUrl);

            var variableManager = camForm.variableManager;
            $scope.formioSubmission = submission
            let submissionVariable = {
                name: (camForm.taskId != null) ? camForm.taskId + submissionSuffix : startFormVariableName,
                type: 'Json',
                value: submission,
                valueInfo: {transient: submitAsTransientVariableDefault},
                isDirty: true
            }

            if (customSubmissionVariableName != null) {
                submissionVariable.name = customSubmissionVariableName
            }

            submissionVariable.name += (suffix != null) ? suffix : ''

            if (transientVariableParamValue != null) {
                if (transientVariableParamValue === 'true') {
                    submissionVariable.valueInfo = {transient: true}
                }
            }

            variableManager.createVariable(submissionVariable);
        }

        /**
         * Used to clean buttons in schemas / typically subforms
         * Hides Submit buttons
         * Hides all buttons if hideAllButtons equals 'true' (string)
         *
         */
        function cleanButtons(schema, hideAllButtons){
            FormioUtils.eachComponent(schema.components, component => {
                if (component.type === 'button') {
                    if (hideAllButtons === 'true') {
                        component.hidden = true
                    } else {
                        if (component.action === 'submit' || !component.action) {
                            component.hidden = true
                        }
                    }
                }
            })
        }

        /**
         * Injects subForm components into container form components that have the subForm property
         * @param schema
         * @returns {Promise<undefined>}
         */
        function setupContainerSubForms(schema){
            return new Promise(resolve => {
                let promises = []
                FormioUtils.eachComponent(schema.components, component => {
                    if (component.type === 'container' && _.has(component.properties, subFormKey)){
                        let sub = getSchema(camForm.processDefinitionId, '?' + component.properties[subFormKey]).then(subSchema => {
                            cleanButtons(subSchema, component.properties[subFormHideButtonsKey])
                            component.components = component.components.concat(subSchema.components)
                        })
                        promises.push(sub)
                    }
                }, true)
                Promise.all(promises).then(() => {
                    resolve()
                })
            })
        }


        camForm.on('form-loaded', function () {
            $scope.options.hideCompleteButton = true
            getSchema(camForm.processDefinitionId, camForm.options.formUrl).then(schema => {
                let varNames = []
                FormioUtils.eachComponent(schema.components, component => {
                    if (_.has(component.properties, fetchVariableKey)){
                        varNames.push(component.properties[fetchVariableKey])
                    }
                }, true)

                return {schema: schema, varNames: varNames}

            }).then(details => {
                return getVariables(details.varNames).then(variables => {
                    //TODO should variables be stored in camForm or just the scope?
                    // This is done so we can access the variables from formio for default value population
                    $scope.camForm.formioVariables = variables
                    return details
                })

            }).then(details => {
                return setupContainerSubForms(details.schema).then(() => {
                    return details
                })
            }).then(details => {
                if (isStartForm($scope.camForm)){
                    hideStartFormStartButton()
                } else {
                    hideUserTaskFormCompleteButton()
                }

                try {
                    Formio.Templates.framework = 'bootstrap3'
                    Formio.createForm(document.getElementById(formioDivId), details.schema)
                        .then(function (form) {
                            $scope.formioForm = form
                            form.nosubmit = true;

                            form.submission = getDefaultValues($scope.camForm.formioVariables, form.submission.data, form.schema)

                            camForm.on('submit-failed', function (evt, res) {
                                clearVariableManager()
                                console.log("Camunda-Formio Submission Validation Error")
                                let error = res[0].message

                                let formioError = (function() {
                                    let content = error.slice(error.indexOf(':') + 1)
                                    if (isJsonString(content)){
                                        return {validationError: true, error: JSON.parse(content) }
                                    } else {
                                        return {validationError: false, error: content}
                                    }
                                })()

                                if (formioError.validationError){
                                    $scope.formioForm.showErrors(formioError.error.details)
                                } else {
                                    $scope.formioForm.showErrors([formioError.error])
                                }

                                $scope.formioForm.emit('submitError')
                            });

                            camForm.on('submit-success', function (evt) {
                                $scope.formioForm.emit('submitDone', $scope.formioSubmission)
                            })

                            camForm.on('error-success', function() {
                                angular.element('.task-card').scope().dismissTask()
                                // https://github.com/camunda/camunda-bpm-webapp/blob/master/ui/cockpit/src/modules/utils/notifications.js#L28-L37
                                inject(['Notifications', function(Notifications) {
                                    let taskId = $scope.camForm.taskId

                                    Notifications.addMessage({
                                        type: 'info',
                                        duration: 5000,
                                        status: 'BPMN Error Created',
                                        message: 'A BPMN Error was triggered for Task ' + taskId
                                    });
                                }]);
                            })

                            camForm.on('escalation-success', function() {
                                if ($scope.camForm.escalationInterrupt){
                                    angular.element('.task-card').scope().dismissTask()
                                } else {
                                    // Clear variable manager if it was a non-interrupting escalation
                                    clearVariableManager()
                                }
                                // https://github.com/camunda/camunda-bpm-webapp/blob/master/ui/cockpit/src/modules/utils/notifications.js#L28-L37
                                inject(['Notifications', function(Notifications) {
                                    let taskId = $scope.camForm.taskId

                                    Notifications.addMessage({
                                        type: 'info',
                                        duration: 5000,
                                        status: 'Escalation Created',
                                        message: 'Escalation was triggered for Task ' + taskId
                                    });
                                }]);
                            })

                            camForm.on('escalation-failed', function (evt, res) {
                                console.log('Camunda-Formio Escalation failed: ' + res[0])
                                clearVariableManager()
                                $scope.formioForm.showErrors(res[0])

                                $scope.formioForm.emit('submitError')
                            });

                            camForm.on('error-failed', function (evt, res) {
                                console.log('Camunda-Formio BPMN Error failed: ' + res[0])
                                clearVariableManager()
                                $scope.formioForm.showErrors(res[0])

                                $scope.formioForm.emit('submitError')
                            });

                            form.on('bpmn-escalation', submission => {
                                setupSubmissionVariables(submission, bpmnEscalationInterruptSuffixDefault)
                                $scope.camForm.escalationInterrupt = true
                                if (_.has(submission, bpmnEscalationCodeKey) && !_.isEmpty(submission[bpmnEscalationCodeKey])){
                                    $scope.camForm.escalate(submission[bpmnEscalationCodeKey])
                                } else {
                                    $scope.camForm.escalate(bpmnEscalationDefaultEscalationCode)
                                }
                            })

                            form.on('bpmn-escalation-noninterrupt', submission => {
                                setupSubmissionVariables(submission, bpmnEscalationNonInterruptSuffixDefault)
                                $scope.camForm.escalationInterrupt = false
                                if (_.has(submission, bpmnEscalationCodeKey) && !_.isEmpty(submission[bpmnEscalationCodeKey])){
                                    $scope.camForm.escalate(submission[bpmnEscalationCodeKey])
                                } else {
                                    $scope.camForm.escalate(bpmnEscalationDefaultEscalationCode)
                                }
                            })

                            form.on('bpmn-error', submission => {
                                setupSubmissionVariables(submission)

                                let errorMessage = (() => {
                                    if (_.has(submission, bpmnErrorMessageKey)){
                                        return submission[bpmnErrorMessageKey]
                                    } else {
                                        return bpmnErrorDefaultErrorMessage
                                    }
                                })()

                                if (_.has(submission, bpmnErrorCodeKey)){
                                    $scope.camForm.error(submission[bpmnEscalationCodeKey], errorMessage)
                                } else {
                                    $scope.camForm.error(bpmnErrorDefaultErrorCode, errorMessage)
                                }
                            })

                            form.on('submit', submission => {
                                setupSubmissionVariables(submission)

                                $scope.complete()
                            })

                        });
                } catch (e) {
                    log.error(e)
                }
            })
        });
    </script>
</form>